﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Hygeia.Server.DAL
{
    public class UnitOfWork<TContext> : IUnitOfWork where TContext : DbContext, new()
    {
        private TContext _context;
        private Hashtable _repositories;

        public UnitOfWork(TContext t)
        {
            _context = t;
        }
        public TContext Context
        {
            get
            {
                return _context;
            }
        }

        /// <summary>  
        /// 取得某一個Entity的Repository。  
        /// 如果沒有取過，會initialise一個  
        /// 如果有就取得之前initialise的那個。  
        /// </summary>  
        /// <typeparam name="TRepository">此Context裡面的Entity Type</typeparam>  
        /// <returns>Entity的IRepository</returns>
        public IRepository<TRepository> GetRepository<TRepository>() where TRepository : class
        {
            if (_repositories == null)
            {
                _repositories = new Hashtable();
            }

            var typeName = typeof(TRepository).Name;

            if (!_repositories.ContainsKey(typeName))
            {
                Assembly assy = Assembly.GetExecutingAssembly();
                var repositoryType = assy.GetType(assy.FullName.Split(new char[] {',' })[0] + "." + typeName + "Repository`1", true, true);
                Type[] typeArgs = { typeof(TContext)};
                repositoryType = repositoryType.MakeGenericType(typeArgs);
                var repositoryInstance = Activator.CreateInstance(repositoryType, _context);

                _repositories.Add(typeName, repositoryInstance);
            }

            return (IRepository<TRepository>)_repositories[typeName];
        }

        public void ExecuteProcedure(string procedureCommand, params object[] sqlParams)
        {
            _context.Database.ExecuteSqlCommand(procedureCommand, sqlParams);
        }

        public void SaveChanges()
        {
            if (Context.GetValidationErrors().Any())
            {
                // TODO: move validation errors into domain level exception and then throw it instead of EF related one
            }
            Context.SaveChanges();
        }

        private bool disposed = false;
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if(disposing)
                {
                    if (_context != null)
                        _context.Dispose();
                }
            }
            this.disposed = true;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}
